package com.zhuoyue.biz.core.api.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.zhuoyue.biz.core.api.dao.DeviceDao;
import com.zhuoyue.biz.core.api.dao.GatewayDao;
import com.zhuoyue.biz.core.api.dao.InfraredDeviceDao;
import com.zhuoyue.biz.core.api.domain.DeviceDO;
import com.zhuoyue.biz.core.api.domain.InfraredDeviceDO;
import com.zhuoyue.biz.core.api.dto.rep.LinkageRelationRepDTO;
import com.zhuoyue.biz.core.api.dto.rep.AcEndDeviceMessage;
import com.zhuoyue.biz.core.api.dto.req.LinkageRelationReqDTO;
import com.zhuoyue.biz.core.api.service.GatewayRedisService;
import com.zhuoyue.biz.core.command.model.BaseParams;
import com.zhuoyue.common.exception.ErrorType;
import com.zhuoyue.common.exception.ZyServerException;
import com.zhuoyue.common.utils.CmdUtils;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zhuoyue.biz.core.api.dao.RelationDeviceMapper;
import com.zhuoyue.biz.core.api.domain.RelationDevice;

/**
 * @author wjming
 * @ClassName: RelationDeviceService
 * @Description: 联动关系业务逻辑
 * @Date 2021-08-06 18:20
 */
@Service
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
@Slf4j
public class RelationDeviceService extends ServiceImpl<RelationDeviceMapper, RelationDevice> {
    /**
     * 属于zigbee类型设备
     */
    private static final String TYPE_ZIGBEE = "zigbee";
    /**
     * 属于红外类型设备
     */
    private static final String TYPE_INFRARED = "infrared";
    /**
     * 属于wifi设备
     */
    private static final String TYPE_WIFI = "wifi";
    /**
     * 温控面板(三合一面:空调、地暖、新风)
     */
    private static String DEVICE_TYPE = "0FAC0502";
    /**
     * 添加
     */
    private static final String STATUS_ADD = "add";
    /**
     * 删除
     */
    private static final String STATUS_REMOVE = "remove";

    private final InfraredDeviceDao infraredDeviceDao;
    private final DeviceDao deviceDao;
    private final RelationDeviceMapper relationMapper;
    private final GatewayRedisService gatewayRedisService;

    /**
     * 此方法目前只需要管理zigbee和红外
     *
     * @param startDeviceId
     * @return
     */
    public LinkageRelationRepDTO getByStartId(String startDeviceId) {
        List<RelationDevice> list = list(Wrappers.<RelationDevice>lambdaQuery().eq(RelationDevice::getStartDeviceId, startDeviceId).orderByDesc(RelationDevice::getCreateTime));
        List<AcEndDeviceMessage> acEndDeviceMessages = new ArrayList<>(8);
        list.forEach(l -> {
            if (TYPE_ZIGBEE.equals(l.getEndDeviceType())) {
                DeviceDO de = deviceDao.selectZigbeeNameAndRoomNameById(l.getEndDeviceId());
                if (de == null) {
                    throw new ZyServerException(ErrorType.INVALID_PARAMS.getCode(), "设备不存在");
                }
                AcEndDeviceMessage acEndDeviceMessage = new AcEndDeviceMessage(l.getId(), startDeviceId, de.getId(), de.getName(), de.getRoomName());
                acEndDeviceMessages.add(acEndDeviceMessage);
            }

            if (TYPE_INFRARED.equals(l.getEndDeviceType())) {
                InfraredDeviceDO inf = infraredDeviceDao.selectByIdAndRoomName(l.getEndDeviceId());
                if (inf == null) {
                    throw new ZyServerException(ErrorType.INVALID_PARAMS.getCode(), "设备不存在");
                }
                AcEndDeviceMessage acEndDeviceMessage = new AcEndDeviceMessage(l.getId(), startDeviceId, inf.getId(), inf.getName(), inf.getRoomName());
                acEndDeviceMessages.add(acEndDeviceMessage);
            }
        });


        LinkageRelationRepDTO linkageRelationRep = LinkageRelationRepDTO.builder().acEndDeviceMessage(acEndDeviceMessages).build();
        return linkageRelationRep;
    }

    /**
     * 添加联控关系
     * zigbee+红外
     *
     * @param linkReq
     * @return
     */
    public LinkageRelationRepDTO addRelation(LinkageRelationReqDTO linkReq) {
        isNull(linkReq);

        if (linkReq.getStartDeviceType().equalsIgnoreCase(linkReq.getEndDeviceType())) {
            throw new ZyServerException(ErrorType.INVALID_PARAMS);
        }

        QueryWrapper<DeviceDO> deviceQue = new QueryWrapper<>();
        if (TYPE_ZIGBEE.equalsIgnoreCase(linkReq.getStartDeviceType())) {
            deviceQue.eq("id", linkReq.getStartDeviceId());
        }
        if (TYPE_ZIGBEE.equalsIgnoreCase(linkReq.getEndDeviceType())) {
            deviceQue.eq("id", linkReq.getEndDeviceId());
        }
        deviceQue.and(i -> i.eq("od_index", "0FAC").eq("device_type", "05").eq("product_type", "02_1"));

        DeviceDO device = deviceDao.selectOne(deviceQue);
        if (device == null) {
            throw new ZyServerException(ErrorType.INVALID_PARAMS);
        }


        isInfrared(linkReq);
        return sendCmdAndReturnRelationRepDTO(linkReq);
    }


    /**
     * zigbee设备的联控
     * zigbee+zigbee
     *
     * @param linkReq
     * @return
     */
    public LinkageRelationRepDTO addZigbeeRelation(LinkageRelationReqDTO linkReq) {
        final int deviceSize = 2;
        if (!TYPE_ZIGBEE.equals(linkReq.getStartDeviceType()) || !TYPE_ZIGBEE.equals(linkReq.getEndDeviceType())) {
            throw new ZyServerException(ErrorType.INVALID_PARAMS);
        }
        QueryWrapper<DeviceDO> deviceQue = new QueryWrapper<>();
        deviceQue.eq("id", linkReq.getStartDeviceId()).or();
        deviceQue.eq("id", linkReq.getEndDeviceId());
        List<DeviceDO> device = deviceDao.selectList(deviceQue);
        if (device.size() != deviceSize) {
            throw new ZyServerException(ErrorType.INVALID_PARAMS);
        }
        return sendCmdAndReturnRelationRepDTO(linkReq);
    }

    /**
     * 发送Cmd命令，组件回应关系信息
     *
     * @param linkReq
     * @return
     */
    private LinkageRelationRepDTO sendCmdAndReturnRelationRepDTO(LinkageRelationReqDTO linkReq) {
        Integer odCode = createOdCode(linkReq.getStartDeviceId());

        RelationDevice relationDevice = new RelationDevice().reqDtoToDo(linkReq, odCode);

        boolean save = save(relationDevice);
        if (!save) {
            throw new ZyServerException(ErrorType.Service_Fail);
        }
        //发送数据帧
        sendCmd(relationDevice.getId(), STATUS_ADD);
        return getByStartId(linkReq.getStartDeviceId());
    }


    /**
     * 判断是否是红外设备
     *
     * @param linkReq
     */
    private void isInfrared(LinkageRelationReqDTO linkReq) {
        QueryWrapper<InfraredDeviceDO> infraredQue = new QueryWrapper<>();
        if (TYPE_INFRARED.equalsIgnoreCase(linkReq.getStartDeviceType())) {
            infraredQue.eq("id", linkReq.getStartDeviceId());
        }
        if (TYPE_INFRARED.equalsIgnoreCase(linkReq.getEndDeviceType())) {
            infraredQue.eq("id", linkReq.getEndDeviceId());
        }
        Integer infrared = infraredDeviceDao.selectCount(infraredQue);
        if (infrared < 1) {
            throw new ZyServerException(ErrorType.INVALID_PARAMS);
        }
    }

    /**
     * 判断设备是否为空
     */
    private void isNull(LinkageRelationReqDTO linkReq) {
        int count = count(Wrappers.<RelationDevice>lambdaQuery()
                .eq(RelationDevice::getStartDeviceId, linkReq.getStartDeviceId())
                .eq(RelationDevice::getStartDeviceType, linkReq.getStartDeviceType())
                .eq(RelationDevice::getEndDeviceId, linkReq.getEndDeviceId())
                .eq(RelationDevice::getEndDeviceType, linkReq.getEndDeviceType()));
        if (count > 0) {
            throw new ZyServerException(ErrorType.INVALID_PARAMS.getCode(), "不能重复绑定");
        }
    }

    /**
     * 删除设备之间的关系信息
     *
     * @param id
     * @return
     */
    @Override
    public boolean removeById(Serializable id) {
        try {
            sendCmd(id, STATUS_REMOVE);
        } catch (Exception e) {
            log.warn("空调控制器联控-->removeById-->数据帧操作失败:{}", e);

        } finally {
            int i = relationMapper.deleteById(id);
            if (i < 1) {
                throw new ZyServerException(ErrorType.Service_Fail.getCode(), "数据不存在或服务器异常");
            }
        }
        return true;
    }


    /**
     * 清空数据
     *
     * @param startDeviceId
     */
    public void removeAll(String startDeviceId) {
        try {
            clearCmd(startDeviceId);
        } catch (Exception e) {
            log.warn("空调控制器联控-->removeAll-->数据帧操作失败:{}", e);
        } finally {
            remove(Wrappers.<RelationDevice>lambdaQuery()
                    .eq(RelationDevice::getStartDeviceId, startDeviceId));
        }
    }


    /**
     * 发送数据帧
     */
    private void sendCmd(Serializable id, String status) {

        //绑定模型
        final String bindMode = "0201${gatewayMac}${deviceMac}1777${odCode}14${sourcePoint}${targetPoint}${targetMac}00000100FF0000000000";
        final String unBindMode = "0201${gatewayMac}${deviceMac}1777${odCode}14FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF";

        RelationDevice acCmdSendParam = getById(id);
        //开始设备是Zigbee(也就三合一面板在前)
        if (TYPE_ZIGBEE.equals(acCmdSendParam.getStartDeviceType()) && TYPE_INFRARED.equals(acCmdSendParam.getEndDeviceType())) {
            //获取
            DeviceDO deviceDoPenal = deviceDao.selectDeviceAndGatewayMac(acCmdSendParam.getStartDeviceId());
            InfraredDeviceDO infrared = infraredDeviceDao.selectById(acCmdSendParam.getEndDeviceId());
            DeviceDO deviceAc = deviceDao.selectById(infrared.getDeviceId());
            String startChildOdCode = acCmdSendParam.getStartChildOdCode() > 10 ? acCmdSendParam.getStartChildOdCode() + "" : "0" + acCmdSendParam.getStartChildOdCode();
            String cmd = "";
            if (STATUS_ADD.equals(status)) {
                String code = AirAddress.returnAddress16(infrared.getCentralAcCode());
                cmd = new AcModeParam(deviceDoPenal.getGatewayMac(), deviceDoPenal.getZigbeeMac(), startChildOdCode,
                        "01", code, deviceAc.getZigbeeMac()).getCmd(bindMode);
            }
            if (STATUS_REMOVE.equals(status)) {
                cmd = new AcModeParam(deviceDoPenal.getGatewayMac(), deviceDoPenal.getZigbeeMac(), startChildOdCode).getCmd(unBindMode);
            }


            gatewayRedisService.writeDataToGateway(deviceDoPenal.getGatewayMac(), cmd);
        }
        //开始设备是红外(空调控制器在前)
        if (TYPE_INFRARED.equals(acCmdSendParam.getStartDeviceType()) && TYPE_ZIGBEE.equals(acCmdSendParam.getEndDeviceType())) {
            InfraredDeviceDO infrared = infraredDeviceDao.selectById(acCmdSendParam.getStartDeviceId());
            DeviceDO deviceAc = deviceDao.selectDeviceAndGatewayMac(infrared.getDeviceId());
            String code = AirAddress.returnAddress16(infrared.getCentralAcCode());
            String startChildOdCode = acCmdSendParam.getStartChildOdCode() > 10 ? acCmdSendParam.getStartChildOdCode() + "" : "0" + acCmdSendParam.getStartChildOdCode();
            //获取
            DeviceDO deviceDoPenal = deviceDao.selectById(acCmdSendParam.getEndDeviceId());
            String cmd = "";
            if (STATUS_ADD.equals(status)) {
                cmd = new AcModeParam(deviceAc.getGatewayMac(), deviceAc.getZigbeeMac(), startChildOdCode,
                        code, "01", deviceDoPenal.getZigbeeMac()).getCmd(bindMode);
            }
            if (STATUS_REMOVE.equals(status)) {
                cmd = new AcModeParam(deviceAc.getGatewayMac(), deviceAc.getZigbeeMac(), startChildOdCode).getCmd(unBindMode);
            }
            gatewayRedisService.writeDataToGateway(deviceAc.getGatewayMac(), cmd);

        }
        //开始设备是红外(此方法通用)
        if (TYPE_ZIGBEE.equals(acCmdSendParam.getStartDeviceType()) && TYPE_ZIGBEE.equals(acCmdSendParam.getEndDeviceType())) {
            //获取
            DeviceDO deviceStart = deviceDao.selectDeviceAndGatewayMac(acCmdSendParam.getStartDeviceId());
            DeviceDO deviceEnd = deviceDao.selectById(acCmdSendParam.getEndDeviceId());
            String startChildOdCode = acCmdSendParam.getStartChildOdCode() > 10 ? acCmdSendParam.getStartChildOdCode() + "" : "0" + acCmdSendParam.getStartChildOdCode();
            String cmd = "";
            String startProd = deviceStart.getProductType();
            String endProd = deviceEnd.getProductType();
            String sourcePoint = "01";
            String targetPoint = "01";
            boolean containsStart = StringUtils.contains(startProd, "_");
            boolean containsEnd = StringUtils.contains(endProd, "_");
            if (containsStart) {
                sourcePoint = "0" + StringUtils.substringAfter(startProd, "_");

            }
            if (containsEnd) {
                targetPoint = "0" + StringUtils.substringAfter(endProd, "_");

            }
            if (STATUS_ADD.equals(status)) {
                cmd = new AcModeParam(deviceStart.getGatewayMac(), deviceStart.getZigbeeMac(), startChildOdCode,
                        sourcePoint, targetPoint, deviceEnd.getZigbeeMac()).getCmd(bindMode);
            }
            if (STATUS_REMOVE.equals(status)) {
                cmd = new AcModeParam(deviceStart.getGatewayMac(), deviceStart.getZigbeeMac(), startChildOdCode).getCmd(unBindMode);
            }


            gatewayRedisService.writeDataToGateway(deviceStart.getGatewayMac(), cmd);

        }


    }


    private void clearCmd(String startDeviceId) {
        final String clearMode = "0201${gatewayMac}${deviceMac}17772103FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF";
        DeviceDO deviceDoPenal = deviceDao.selectDeviceAndGatewayMac(startDeviceId);
        if (deviceDoPenal == null) {
            InfraredDeviceDO infraredDevice = infraredDeviceDao.selectById(startDeviceId);
            if (infraredDevice == null) {
                throw new ZyServerException(ErrorType.OBJECT_NOT_FOUND);
            }
            deviceDoPenal = deviceDao.selectDeviceAndGatewayMac(infraredDevice.getDeviceId());
        }
        String cmd = new AcModeParam(deviceDoPenal.getGatewayMac(), deviceDoPenal.getZigbeeMac(), "FF").getCmd(clearMode);
        gatewayRedisService.writeDataToGateway(deviceDoPenal.getGatewayMac(), cmd);
    }

    private synchronized Integer createOdCode(String startDeviceId) {
        List<Integer> codes = relationMapper.selectCode(startDeviceId);
        if (codes.size() >= 8) {
            throw new ZyServerException(ErrorType.Service_Fail.getCode(), "联控配置已满");
        }
        if (CollectionUtils.isEmpty(codes)) {
            return 1;
        }

        for (int i = 1; i <= 8; i++) {
            if (codes.size() < i) {
                return i;
            }
            if (i != codes.get(i - 1)) {
                return i;
            }
        }

        return 1;
    }


}


@Data
class AcModeParam extends BaseParams {
    private String odCode;
    private String sourcePoint;
    private String targetPoint;
    private String targetMac;

    public AcModeParam(String gatewayMac, String deviceMac, String odCode, String sourcePoint, String targetPoint, String targetMac) {
        super(gatewayMac, deviceMac);
        this.odCode = odCode;
        this.sourcePoint = sourcePoint;
        this.targetPoint = targetPoint;
        this.targetMac = targetMac;
    }

    public AcModeParam(String gatewayMac, String deviceMac, String odCode) {
        super(gatewayMac, deviceMac);
        this.odCode = odCode;
    }

}

/**
 * 地址计算
 */
class AirAddress {

    /**
     * @param addressStr
     * @return
     */
    public static String returnAddress16(String addressStr) {
        String[] sStr = addressStr.split("_");
        int outAcAddress = Integer.parseInt(sStr[0]);
        int innerAcAddress = Integer.parseInt(sStr[1]);
        //真实的空调地址+1,因为不能为0 所以全体+1
        int s = (outAcAddress - 1) * 16 + (innerAcAddress) + 1;
        return CmdUtils.intToHexTwo(s);
    }
}